package cc.telecomdigital.tdstock.Services;

import cc.telecomdigital.tdstock.Framework.UniqueItem.TDStockLog;

import com.lightstreamer.ls_client.ExtendedTableInfo;
import com.lightstreamer.ls_client.HandyTableListener;
import com.lightstreamer.ls_client.LSClient;
import com.lightstreamer.ls_client.PushConnException;
import com.lightstreamer.ls_client.PushServerException;
import com.lightstreamer.ls_client.PushUserException;
import com.lightstreamer.ls_client.SubscrException;
import com.lightstreamer.ls_client.SubscribedTableKey;
import com.lightstreamer.ls_client.UpdateInfo;

//Per Subscription Handling
public class LS_ClientSubscriberHandler implements HandyTableListener
{
	// Internal Client Control
	public interface ILS_ClientSubscriber_Response
	{
		/** On Update Reference for LS Client **/
		void onUpdate(String iIdentifier, int iSchemaIndex,
				String iSchemaValue, UpdateInfo iUpdateInfo);
	}

	public interface ILS_ConnectionListener
	{
		/** Get the ls client object **/
		LSClient GetClient();

		/** Check if it is connected **/
		boolean IsConnected();
	}

	/** Internal Identifier to be recognized by the outsider **/
	private String						identifier;
	private ILS_ConnectionListener		controller;
	/** Listener for data update **/
	private ILS_ClientSubscriber_Response	dataListener;
	private SubscribedTableKey[]			itemKeys;
	private SubscribedTableKey			tableKey;

	//a lock to prevent simultaneously access
	//private static Object padLock           = new Object();
	private final boolean				debugMode				= false;
	private final double				REQUEST_FREQUENCY_TIME	= 1.0d;	//1 second
	//private final int                       MAX_RD_BUFFER_SIZE   		= 0;		// unlimited buffer	
	//private final int                       MAX_RD_BUFFER_SIZE 		= 1024*32; 

	/** Create a subscription Data Handler
      * @param iId - Unique ID to be pass to listener
	 * @param iControl - Connection Control, to known whether the connection status of the client
	 * @param iDataListener - Listening for data Update (Imported data)
	 */
	public LS_ClientSubscriberHandler(String iId,
			ILS_ConnectionListener iControl,
			ILS_ClientSubscriber_Response iDataListener)
	{
		identifier = iId;
		controller = iControl;
		dataListener = iDataListener;
		tableKey = null;
		itemKeys = null;		
	}

	/** Let outsider to read our identifier **/
	public String GetIdentifier()
	{
		return identifier;
	}

	private static final int retryMaxCount = 2;
	
	public boolean SubscribeObject(LS_SubscriptionItem iSubItem)
	{
		if (debugMode)
			TDStockLog.i(identifier, "**SubscribeItems: " + iSubItem.toString());
		
		//One object could occupy request
		if ((tableKey != null) || (itemKeys != null))
			return false;
		
		//synchronized(padLock)
		{	
			if (controller.IsConnected() == false)
				return false;
				
			boolean status;
			int     trialCount;
			
			status  = false;
			trialCount = 0;
			while(trialCount < retryMaxCount)
			{
				if (iSubItem.IsTableRequest())
					status = SubscribeTable(iSubItem);
				else
					status = SubscribeItems(iSubItem);
				
				if (status == true)
					break;
				
				trialCount++;
				if (debugMode)
					TDStockLog.i(identifier, "**SubscribeItems Retry Count: " + trialCount);							
			}				
			
			
			return status;
		}
	}

	private boolean SubscribeItems(LS_SubscriptionItem iSubItem)
	{
		try
		{
			ExtendedTableInfo tableInfo;
			tableInfo = new ExtendedTableInfo(iSubItem.GetSubscribeItems()
					.toArray(), ExtendedTableInfo.MERGE, iSubItem.GetSubscribeSchema()
					.toArray(), true);
			tableInfo.setDataAdapter(iSubItem.GetAdapter());
			tableInfo.setRequestedMaxFrequency(REQUEST_FREQUENCY_TIME);
			//tableInfo.setRequestedBufferSize(MAX_RD_BUFFER_SIZE);	

			try
			{
				if (debugMode)
					TDStockLog.i(identifier, "SubscribeItems: Start");
				
				//Subscription report to self
				if (controller.GetClient() == null)
					return false;
				
				itemKeys = controller.GetClient().subscribeItems(tableInfo,
						  	  this);
			}
			catch (PushServerException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "SubscribeItems=>PushServerException: " + e.toString());
				return false;
			}
			catch (PushUserException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "SubscribeItems=>PushUserException: " + e.toString());
				return false;
			}
			catch (PushConnException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier,"SubscribeItems=>PushConnException: " + e.toString());
				return false;
			}
			catch (Exception e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier,"SubscribeItems=>Exception: " + e.toString());
				return false;
			}			
		}
		catch (SubscrException e)
		{
			// Exception when creating a tableInfo
			e.printStackTrace();
			TDStockLog.e(identifier, "SubscribeItems=>SubscrException: " + e.toString());
			return false;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			TDStockLog.e(identifier,"SubscribeItems=>Exception: " + e.toString());
			return false;
		}		
		
		if (debugMode)
			TDStockLog.i(identifier, "**SubscribeItems: Done");

		return true;
	}

	private boolean SubscribeTable(LS_SubscriptionItem iSubItem)
	{
		try
		{
			ExtendedTableInfo tableInfo;
			tableInfo = new ExtendedTableInfo(iSubItem.GetSubscribeItems()
					.toArray(), ExtendedTableInfo.MERGE, iSubItem.GetSubscribeSchema()
					.toArray(), true);
			tableInfo.setDataAdapter(iSubItem.GetAdapter());
			//tableInfo.setRequestedBufferSize(MAX_RD_BUFFER_SIZE);

			try
			{
				if (debugMode)
					TDStockLog.i(identifier, "SubscribeTable: Start");

				LSClient clientControl;

				clientControl = controller.GetClient();
				if (clientControl == null)
					return false;

				tableKey = clientControl.subscribeTable(tableInfo,
						this, false);
			}
			catch (PushServerException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "SubscribeTable=>PushServerException: " + e.toString());
				return false;
			}
			catch (PushUserException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "SubscribeTable=>PushUserException: " + e.toString());
				return false;
			}
			catch (PushConnException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "SubscribeTable=>PushConnException: " + e.toString());
				return false;
			}
			catch (Exception e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier,"SubscribeTable=>Exception: " + e.toString());
				return false;
			}
		}
		catch (SubscrException e)
		{
			e.printStackTrace();
			TDStockLog.e(identifier, "SubscribeTable=>SubscrException: " + e.toString());
			return false;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			TDStockLog.e(identifier,"SubscribeTable=>Exception: " + e.toString());
			return false;
		}	
		
		if (debugMode)
			TDStockLog.i(identifier, "**SubscribeTable: Done");

		return true;
	}

	//If ask for unsubscribe, we would trust try our best
	//and erase our record
	public void UnsubscribeObject()
	{
		if ((tableKey == null) && (itemKeys == null))
			return;

		//synchronized(padLock)
		{
			try
			{
				if (debugMode)
					TDStockLog.i(identifier, "UnsubscribeObject: Start");

				LSClient clientControl;

				clientControl = controller.GetClient();
				if ((clientControl != null) && controller.IsConnected())
				{
					if (tableKey != null)
						clientControl.unsubscribeTable(tableKey);
					else if (itemKeys != null)
						clientControl.unsubscribeTables(itemKeys);
				}
			}
			catch (SubscrException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "UnsubscribeObject=>SubscrException: " + e.toString());
			}
			catch (PushServerException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "UnsubscribeObject=>PushServerException: " + e.toString());
			}
			catch (PushConnException e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier, "UnsubscribeObject=>PushConnException: " + e.toString());
			}
			catch (Exception e)
			{
				e.printStackTrace();
				TDStockLog.e(identifier,"UnsubscribeObject=>Exception: " + e.toString());
			}
			
			finally
			{
				tableKey = null;
				itemKeys = null;
			}

			if (debugMode)
				TDStockLog.i(identifier, "**Unsubscribe: Done");
		}
	}

	/** If there is no connection, we are enforce to drop all the value.
	  * A new connection will establish when there are network, so this 
	  * would not cause any trouble
	 */
	public void Dispose()
	{
		UnsubscribeObject();
//		if (controller.IsConnected())
//			UnsubscribeObject();
//		else
//		{
//			synchronized(padLock)
//			{
//				try
//				{
//					LSClient clientControl;
//
//					clientControl = controller.GetClient();
//					if (clientControl != null)
//					{
//						//Enforce a kill of the subscription
//						if (tableKey != null)
//							clientControl.forceUnsubscribeTable(tableKey);
//						else if (itemKeys != null)
//						{
//							for (SubscribedTableKey item: itemKeys)
//								clientControl.forceUnsubscribeTable(item);					
//						}
//					}
//				}
//				catch (SubscrException e)
//				{
//					e.printStackTrace();
//					TDStockLog.e(identifier, "Dispose=>SubscrException: " + e.toString());
//				}
//				catch (PushServerException e)
//				{
//					e.printStackTrace();
//					TDStockLog.e(identifier, "Dispose=>PushServerException: " + e.toString());					
//				}
//				catch (PushConnException e)
//				{
//					e.printStackTrace();
//					TDStockLog.e(identifier, "Dispose=>PushConnException: " + e.toString());					
//				}
//			}
//		}
//
//		tableKey = null;
//		itemKeys = null;
		dataListener = null;
		identifier   = null;
	}

	//Update on connection
	@Override
	public void onRawUpdatesLost(int iSchemaIndex, String iSchemaValue,
			int arg2)
	{
		if (debugMode)
			TDStockLog.i(identifier, "onRawUpdatesLost: " + iSchemaIndex + ", "
					+ iSchemaValue + ", " + arg2);
	}

	@Override
	public void onSnapshotEnd(int iSchemaIndex, String iSchemaValue)
	{
		if (debugMode)
			TDStockLog.i(identifier, "onSnapshotEnd: " + iSchemaIndex + ", "
					+ iSchemaValue);
	}

	@Override
	public void onUnsubscr(int iSchemaIndex, String iSchemaValue)
	{
		tableKey = null;
		itemKeys = null;
		if (debugMode)
			TDStockLog.i(identifier, "onUnsubscr=>Index:" + iSchemaIndex + ", Value:"
					+ iSchemaValue);
	}

	@Override
	public void onUnsubscrAll()
	{
		tableKey = null;
		itemKeys = null;
		if (debugMode)
			TDStockLog.i(identifier, "onUnsubscrAll");
	}

	@Override
	public void onUpdate(int iSchemaIndex, String iSchemaValue,
			UpdateInfo iUpdateInfo)
	{
		//Pass the data to the requested dataListener
		if ((dataListener != null) && (identifier != null))
			dataListener.onUpdate(identifier, iSchemaIndex, iSchemaValue, iUpdateInfo);
		else
			//Perform a self dispose
			Dispose();
	}

}
